float4x4 World;
float4x4 View;
float4x4 Projection;
float3 CameraPosition;

//light properties
float3 LightPosition;
float4 AmbientLightColour;
float4 DiffuseLightColour;
float4 SpecularLightColour;

int lightNum = 0;

struct Light 
{
    float4 colour;
    float3 position;
    float radius;
};

Light lights[40];

float LightRadius;

//material properties
float SpecularPower;
float SpecularIntensity;

texture Texture;
texture NormalMap;
texture SpecularMap;

sampler DiffuseSampler = sampler_state
{
    Texture = (Texture);
    MAGFILTER = LINEAR;
    MINFILTER = LINEAR;
    MIPFILTER = LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

sampler NormalSampler = sampler_state
{
    Texture = (NormalMap);
    MagFilter = LINEAR;
    MinFilter = LINEAR;
    Mipfilter = LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

sampler SpecularSampler = sampler_state
{
    Texture = (SpecularMap);
    MagFilter = LINEAR;
    MinFilter = LINEAR;
    Mipfilter = LINEAR;
    AddressU = Wrap;
    AddressV = Wrap;
};

struct VertexShaderInput
{
    float4 Position : POSITION0;
	float3 Normal : NORMAL0;
    float2 TexCoord : TEXCOORD0;
    float3 Binormal : BINORMAL0;
    float3 Tangent : TANGENT0;
};

struct VertexShaderOutput
{
     float4 Position : POSITION0;
     float3 WorldNormal : TEXCOORD0;
     float3 WorldPosition : TEXCOORD1;
     float2 TexCoord : TEXCOORD2;
     float3x3 TangentToWorld : TEXCOORD3;
};

struct PixelShaderInput
{
     float3 WorldNormal : TEXCOORD0;
     float3 WorldPosition : TEXCOORD1;
     float2 TexCoord : TEXCOORD2;
};

VertexShaderOutput PerPixelVertexShader( VertexShaderInput input )
{
     VertexShaderOutput Output;

     //generate the world-view-projection matrix
     float4x4 wvp = mul(mul(World, View), Projection);
     
     //transform the input position to the output
     Output.Position = mul(input.Position, wvp);

     Output.WorldNormal =  mul(input.Normal, World);
     
     float4 WorldPosition =  mul(input.Position, World);
     Output.WorldPosition = WorldPosition / WorldPosition.w;
     
     Output.TexCoord = input.TexCoord;
     
     Output.TangentToWorld[0] = mul(input.Tangent, World);
	 Output.TangentToWorld[1] = mul(input.Binormal, World);
     Output.TangentToWorld[2] = mul(input.Normal, World);

     //return the output structure
     return Output;
}

float4 PerPixelPhongPixelShader(VertexShaderOutput input) : COLOR
{
	half4 TexColour;
	
	TexColour = tex2D(DiffuseSampler, input.TexCoord);
    
    float4 Colour = {0.0f, 0.0f, 0.0f, 0.0f};
    

    for(int i = 0; i < lightNum; i++)
	{
		float Attenuation = 0.0f;
		float3 lightVector = lights[i].position - input.WorldPosition;
	    
		Attenuation = saturate(1.0f - length(lightVector)/lights[i].radius); 
		
		float4 SpecularAttributes = tex2D(SpecularSampler, input.TexCoord);
	    
		// read the normal from the normal map
		float3 NormalFromMap = tex2D(NormalSampler, input.TexCoord);

		//transform to [-1,1]
		NormalFromMap = 2.0f * NormalFromMap - 1.0f;

		//transform into world space
		NormalFromMap = mul(NormalFromMap, input.TangentToWorld);

		//normalize the result
		NormalFromMap = normalize(NormalFromMap);
		
		
		//calculate per-pixel diffuse
		float3 DirectionToLight = normalize(lights[i].position - input.WorldPosition);
		
		float DiffuseIntensity = saturate(dot(DirectionToLight, NormalFromMap));
		float4 Diffuse = lights[i].colour * DiffuseIntensity;

		//calculate Phong components per-pixel
		float3 ReflectionVector = normalize(reflect(-DirectionToLight, NormalFromMap));
		
		//camera-to-surface vector
		float3 DirectionToCamera = normalize(CameraPosition - input.WorldPosition);
	     
		//calculate specular component
		float4 Specular = SpecularLightColour * SpecularAttributes.r * pow(saturate(dot(ReflectionVector, DirectionToCamera)), SpecularAttributes.a * 255);  
	        
		//all colour components are summed in the pixel shader
		Colour += (Specular * Attenuation  + (Diffuse * Attenuation * TexColour)) * 5;
    }

    Colour.a = 1.0;
     
    return Colour;
}

technique PerPixelPhong
{   
    pass P0
    {
        VertexShader = compile vs_3_0 PerPixelVertexShader();              
        PixelShader = compile ps_3_0 PerPixelPhongPixelShader();
    }
}